package org.pentaho.huaweicloud.obs.provider;

import com.obs.services.*;
import com.obs.services.exception.ObsException;
import com.obs.services.internal.security.BasicSecurityKey;
import com.obs.services.model.ISecurityKey;
import com.obs.services.model.ListBucketsRequest;
import com.obs.services.model.ObsBucket;
import org.apache.commons.vfs2.FileSystemOptions;
import org.pentaho.di.connections.ConnectionManager;
import org.pentaho.di.connections.vfs.BaseVFSConnectionProvider;
import org.pentaho.di.connections.vfs.VFSRoot;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.huaweicloud.obs.OBSDetails;
import org.pentaho.obs.vfs.OBSFileProvider;
import org.pentaho.obscommon.OBSCommonFileSystemConfigBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

@SuppressWarnings( "WeakerAccess" )
public class OBSProvider extends BaseVFSConnectionProvider<OBSDetails> {

  private static final String ACCESS_KEY_SECRET_KEY = "0";
  public static final String NAME = "HuaweiCloud OBS";
  private Supplier<ConnectionManager> connectionManagerSupplier = ConnectionManager::getInstance;

  private LogChannelInterface log = new LogChannel( this );

  @Override
  public Class<OBSDetails> getClassType() {
    return OBSDetails.class;
  }

  @Override
  public FileSystemOptions getOpts( OBSDetails obsDetails ) {
    VariableSpace space = getSpace( obsDetails );
    OBSCommonFileSystemConfigBuilder obsCommonFileSystemConfigBuilder =
            new OBSCommonFileSystemConfigBuilder( new FileSystemOptions() );
    obsCommonFileSystemConfigBuilder.setName( getVar( obsDetails.getName(), space ) );
    obsCommonFileSystemConfigBuilder.setAccessKey( getVar( obsDetails.getAccessKey(), space ) );
    obsCommonFileSystemConfigBuilder.setSecretKey( getVar( obsDetails.getSecretKey(), space ) );
    obsCommonFileSystemConfigBuilder.setEndpoint( getVar( obsDetails.getEndpoint(), space ) );
    obsCommonFileSystemConfigBuilder.setConnectionType( getVar( obsDetails.getConnectionType(), space ) );
    return obsCommonFileSystemConfigBuilder.getFileSystemOptions();
  }

  @SuppressWarnings( "unchecked" )
  @Override public List<OBSDetails> getConnectionDetails() {
    return (List<OBSDetails>) connectionManagerSupplier.get().getConnectionDetailsByScheme( getKey() );
  }

  @Override public List<VFSRoot> getLocations( OBSDetails obsDetails ) {
    VariableSpace space = getSpace( obsDetails );
    List<VFSRoot> buckets = new ArrayList<>();
    try {
      IObsClient obs = getOBSClient( obsDetails, space );
      if ( obs != null ) {
        ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
        for ( ObsBucket bucket : obs.listBuckets(listBucketsRequest) ) {
          buckets.add( new VFSRoot( bucket.getBucketName(), bucket.getCreationDate() ) );
        }
      }
    } catch ( Exception e ) {
      log.logError( e.getMessage(), e );
    }
    return buckets;
  }

  @Override
  public String getName() {
    return NAME;
  }

  @Override
  public String getKey() {
    return OBSFileProvider.SCHEME;
  }

  @Override public String getProtocol( OBSDetails obsDetails ) {
    return OBSFileProvider.SCHEME;
  }


  @Override public boolean test( OBSDetails obsDetails ) throws KettleException {
    VariableSpace space = getSpace( obsDetails );
    obsDetails = prepare( obsDetails );
    IObsClient obsClient = getOBSClient( obsDetails, space );
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    try {
      ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
      obsClient.listBuckets(listBucketsRequest);
      return true;
    } catch ( ObsException e ) {
      // expected exception if credentials are invalid.  Log just the message.
      log.logError( e.getMessage() );
      return false;
    } catch ( Exception e ) {
      // unexpected exception, log the whole stack
      log.logError( e.getMessage(), e );
      return false;
    } finally {
      Thread.currentThread().setContextClassLoader( cl );
    }
  }

  private IObsClient getOBSClient( OBSDetails obsDetails, VariableSpace space ) {
    ISecurityKey obsCredentials;
    IObsCredentialsProvider obsCredentialsProvider = null;
    String accessKey = getVar( obsDetails.getAccessKey(), space );
    String secretKey = getVar( obsDetails.getSecretKey(), space );
    String endpoint = getVar( obsDetails.getEndpoint(), space );

    if ( obsDetails.getAuthType().equals( ACCESS_KEY_SECRET_KEY ) ) {
      obsCredentials = new BasicSecurityKey( accessKey, secretKey );
      obsCredentialsProvider = new BasicObsCredentialsProvider( obsCredentials );
    }

    if ( obsCredentialsProvider != null ) {
      BasicSecurityKey credentials = new BasicSecurityKey( accessKey, secretKey );
      ObsConfiguration config = new ObsConfiguration();
      config.setEndPoint( "obs." + endpoint + ".myhuaweicloud.com" );
      return new ObsClient(new BasicObsCredentialsProvider( credentials ), config);
    }
    return null;
  }

}
